package com.stardata.liangshisq.tcslmon.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.stardata.liangshisq.tcslmon.entity.*;
import com.stardata.liangshisq.tcslmon.mapper.ArchItemMapper;
import com.stardata.liangshisq.tcslmon.mapper.ArchPointMapper;
import com.stardata.liangshisq.tcslmon.mapper.BizBsMapper;
import com.stardata.liangshisq.tcslmon.mapper.KcKscMapper;
import com.stardata.liangshisq.tcslmon.service.*;
import com.stardata.liangshisq.tcslmon.util.*;
import lombok.AllArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author Samson Shu
 * @email shush@stardata.top
 * @date 2020/12/14 11:08
 */
@Service
@AllArgsConstructor
public class PassonServiceImpl implements PassonService {
  private final Logger logger = LoggerFactory.getLogger(this.getClass());

  private final SimpMessagingTemplate messagingTemplate;
  private final SysParameterService parameterService;
  private final KcKscService kscService;
  private final ItemQueueService itemQueueService;
  private final OrderQueueService orderQueueService;
  private final ArchItemMapper archItemMapper;
  private final KcKscMapper kcKscMapper;

  private final ArchPointMapper archPointMapper;

  private final BizBsMapper bizBsMapper;

  private final TbKcKscTimeService tbKcKscTimeService;

  @Override
  public List<ProductVo> queryCookedItems(@NotNull Long shopId) {
    List<ProductVo> result = new ArrayList<>();

    List<String> cookedClassIds = parameterService.getListByCode("cookedClassIds", String.class);
    if (cookedClassIds.size()<1) return result;

    QueryWrapper<KcKsc> wrapper = new QueryWrapper<>();
    wrapper.eq("create_shop_id", shopId).lt("kitchen_flg", CommonConstants.KitchenFlg.PRODUCED)
        .apply(String.format("item_id in (select id from arch_item where small_class_id in (%s))",
            String.join(",", cookedClassIds)))
        .orderByAsc("create_time");

    List<KcKsc> kscList = kcKscMapper.selectList(wrapper);
//    int maxCookedItemCount = KitchenQueueUtil.getInstance().getMaxCookedItemCount();
//    int count = 0;
    List<OrderScheduleVo> orderSchedules = parameterService.getListByCode(
        "scheduledOrderQueue", OrderScheduleVo.class);
    List<Long> scheduledBsIds = new ArrayList<>();
    for (OrderScheduleVo orderSchedule : orderSchedules) {
      if (orderSchedule.getScheduled()) {
        scheduledBsIds.add(orderSchedule.getBsId());
      }
    }
    //将后厨菜品列表转换为前端可显示的产品列表
    for (KcKsc kcKsc : kscList) {
      ProductVo productVo = kscService.convertToProductVo(kcKsc);
      if ((productVo != null) && (!scheduledBsIds.contains(kcKsc.getBsId()))) {
        result.add(productVo);
//        count++;
//        if (count >= maxCookedItemCount) break;
      }
    }

    //对于需要合并的菜品进行合并
    KitchenQueryUtil.mergeProductVo4SameBs(result);

    return result;
  }

  @Override
  public List<ProductVo> queryMakingItems(@NotNull Long shopId) {
    List<ProductVo> result = new ArrayList<>();

    //  preparedOrderQueue 预定订单  preparingOrderQueue 制作区
    List<Long> bsIds = parameterService.getListByCode("preparedOrderQueue", Long.class);
    bsIds.addAll(parameterService.getListByCode("preparingOrderQueue", Long.class));

    List<OrderScheduleVo> orderSchedules = parameterService.getListByCode(
            "scheduledOrderQueue", OrderScheduleVo.class);
    for (int i = orderSchedules.size() - 1; i >= 0; i--) {
      OrderScheduleVo scheduleItem = orderSchedules.get(i);
      bsIds.remove(scheduleItem.getBsId());
    }

    if (bsIds.size()<1) return result;

    QueryWrapper<KcKsc> wrapper = new QueryWrapper<>();
    List<String> cookedClassIds = parameterService.getListByCode("cookedClassIds", String.class);
    wrapper.eq("create_shop_id", shopId)
        .lt("kitchen_flg", CommonConstants.KitchenFlg.PRODUCED)
        .in("bs_id", bsIds);
    if (cookedClassIds.size()>0) {
      //需要排除成品熟食
      wrapper.apply(String.format("item_id not in (select id from arch_item where small_class_id in (%s))",
          String.join(",", cookedClassIds)));

    }
    List<KcKsc> resultKscList = kcKscMapper.selectList(wrapper);

    //删除既不是KDS菜品、也不是临时菜品的记录
    KitchenQueryUtil.clearKcKscForNotKitchen(resultKscList, archItemMapper);

    //将菜品项目按照原来的订单排队顺序、下单时间进行排序
//    resultKscList.sort((o1, o2) -> {
//		int flag1 =  o1.getKitchenFlg();
//		int flag2 =  o2.getKitchenFlg();
//		if (flag1 != flag2) {
//			return flag2-flag1;
//		}
//		long item1 =  o1.getItemId();
//		long item2 =  o2.getItemId();
//		if (item1 != item2) {
//			if(item1>item2)
//			return -1;
//		}
//		
////      int idx1 = bsIds.indexOf(o1.getBsId());
////      int idx2 = bsIds.indexOf(o2.getBsId());
////      if (idx1 != idx2) {
////        return idx1-idx2;
////      }
//      LocalDateTime dt1 = o1.getCreateTime();
//      LocalDateTime dt2 = o2.getCreateTime();
//      if (dt1.isBefore(dt2)) {
//        return -1;
//      } else if (dt1.isAfter(dt2)) {
//        return 1;
//      }
//      return 0;
//    });

    //加上临时单品插队的菜品
    List<Long> jumpKscIds = parameterService.getListByCode("jumpItemIds", Long.class);
    if (jumpKscIds.size()>0) {
      List<Long> alreadyKscIds = new ArrayList<>();
      resultKscList.forEach(kcKsc -> alreadyKscIds.add(kcKsc.getId()));
      wrapper.clear();
      wrapper.in("id", jumpKscIds)
          .notIn("id", alreadyKscIds);
      List<KcKsc> jumpKscList = kcKscMapper.selectList(wrapper);
      jumpKscList.sort((o1, o2) -> {
        int idx1 = jumpKscIds.indexOf(o1.getId());
        int idx2 = jumpKscIds.indexOf(o2.getId());
        return idx2-idx1;
      });
      for (KcKsc jumpKsc : jumpKscList) {
    	  if(!bsIds.contains(jumpKsc.getBsId())) {
    		 continue ;
    	  }
    	  if(jumpKsc.getKitchenFlg()==3) {
    		  continue;
    	  }
        resultKscList.add(0, jumpKsc);
      }
    }

    //将最终结果转换后返回给前端
    for (KcKsc kcKsc : resultKscList) {
      ProductVo productVo = kscService.convertToProductVo(kcKsc);
      if (productVo != null) {
        result.add(productVo);
        productVo.setIsJumped(jumpKscIds.contains(kcKsc.getId()));
      }

    }
    //对于需要合并的菜品进行合并
    KitchenQueryUtil.mergeProductVo4SameBs(result);
    
    //将菜品项目按照原来的订单按照状态、菜品、桌号、下单时间排序;  --zzj
    result.sort((o1, o2) -> {
		int flag1 =  o1.getStatus();
		int flag2 =  o2.getStatus();
		if (flag1 != flag2) {
			return flag2-flag1;
		}
//		String item1 =  o1.getItemId();
//		long item2 =  o2.getItemId();
		if (!o1.getItemId().equals(o2.getItemId())) {
			return o1.getItemId().compareTo(o2.getItemId());
		}
		
		if (!o1.getTableNo().equals(o2.getTableNo())) {
			return o1.getTableNo().compareTo(o2.getTableNo());
		}
		
      LocalDateTime dt1 = o1.getOrderTime();
      LocalDateTime dt2 = o2.getOrderTime();
      if (dt1.isBefore(dt2)) {
        return -1;
      } else if (dt1.isAfter(dt2)) {
        return 1;
      }
      return 0;
    });
    
    

    return result;
  }

  @Override
  public void savePassonByList(@NotNull Long operUserId, @NotNull List<Long> kcKscIds,
                               @NotNull Boolean passed) {
    if (passed) {
      kscService.passonByList(operUserId, kcKscIds);
      //对于单品插队的订单，从菜品插队队列中删除
      KitchenQueueUtil.getInstance().removeSingleJumpItems(kcKscIds);
    } else {
      kscService.cancelPassonByList(operUserId, kcKscIds);
    }
    try {
      orderQueueService.refresh();
    } catch (JsonProcessingException e) {
      logger.error("ERROR===> 保存传菜出现异常，JSON格式错误：", e);
    }
    messagingTemplate.convertAndSend("/passon_refresh", "");
  }

  @Override
  public void jumpOrder(@NotNull Long bsId) {
    KitchenQueueUtil kitchenQueueUtil = KitchenQueueUtil.getInstance();
    
    // 2023-10-22 预订单不允许插
    List<OrderScheduleVo> orderSchedules = parameterService.getListByCode(
            "scheduledOrderQueue", OrderScheduleVo.class);
    for (int i = orderSchedules.size() - 1; i >= 0; i--) {
        OrderScheduleVo scheduleItem = orderSchedules.get(i);
        if (scheduleItem.getBsId().equals(bsId)) {
        	 throw new ApiRRException(HttpStatus.BAD_REQUEST, "预订订单，不能插队！");
        }
    }

    //1. 读取"配菜中"订单队列，并判断是否允许再插队
    List<Long> preparingOrderQueue  = parameterService.getListByCode("preparingOrderQueue", Long.class);
    if (preparingOrderQueue.size() >= kitchenQueueUtil.getMaxPreparingSize() +
        kitchenQueueUtil.getMaxJumpOrderCount()) {
      throw new ApiRRException(HttpStatus.BAD_REQUEST, "配菜队列已满，不能再插队！");
    }
    //如果订单已经在"配菜中"状态，则报错
    if (preparingOrderQueue.contains(bsId)) {
      throw new ApiRRException(HttpStatus.BAD_REQUEST, "订单已在配菜中，无需插队！");
    }

    //2. 将插队订单追加到“配菜中”订单队列尾部
    preparingOrderQueue.add(bsId);

    //3. 保存插队结果
    parameterService.saveByCode("preparingOrderQueue", JSONUtil.toJSONString(preparingOrderQueue));
    kitchenQueueUtil.addToJumpOrders(bsId);

    //4. 刷新配菜中订单的菜品排队
    try {
      itemQueueService.refresh();

      //5. 消息通知配菜、传菜界面刷新
      messagingTemplate.convertAndSend("/prepare_refresh", "");
      messagingTemplate.convertAndSend("/passon_refresh", "");
    } catch (JsonProcessingException e) {
      logger.error("ERROR===> 整单插队出现异常，JSON格式错误：", e);
    }
  }

  @Override
  public void jumpItems(@NotNull List<Long> kcKscIds)  {
    for (Long kcKscId : kcKscIds) {
      KcKsc kcKscToJump = kscService.getById(kcKscId);
      if (kcKscToJump == null) {
        throw new ApiRRException(HttpStatus.BAD_REQUEST, "无效的后厨制作流水ID");
      }

      KitchenQueueUtil kitchenQueueUtil = KitchenQueueUtil.getInstance();

      //0. 先找到该菜品归属的配菜窗口
      List<List<Long>> prepareWindowClassIds = kitchenQueueUtil.getPrepareWindowClassIds();
      int windowIndex = prepareWindowClassIds.size()-1; //如果找不到，就插配菜窗口的最后一个窗口
      ArchItem archItem = archItemMapper.selectById(kcKscToJump.getItemId());
      kcKscToJump.setArchItem(archItem);
      if (archItem != null) {
        for (int i = 0; i < prepareWindowClassIds.size(); i++) {
          List<Long> classIds = prepareWindowClassIds.get(i);
          if (classIds.contains(archItem.getSmallClassId())) {
            windowIndex = i;
            break;
          }
        }
      }
      //处理临时菜品
      kcKscToJump.setIsTempItem(archItem ==null || archItem.getIsTempItem());


      //1. 读取配菜窗口队列到windowList
      String windowName =  kitchenQueueUtil.getPrepareWindowMames().get(windowIndex);
      List<WindowItemQueueVo> windowList = parameterService.getListByCode(
          windowName, WindowItemQueueVo.class);

      //2. 判断是否还允许插队，如果不允许则报错

      //2.1 判断配菜窗口是否已满不许插队
      if (windowList.size() >= kitchenQueueUtil.getPrepareWindowSizes().get(windowIndex) +
          kitchenQueueUtil.getMaxJumpItemCount()) {
        throw new ApiRRException(HttpStatus.BAD_REQUEST, "配菜窗口已满，不能再插队！");
      }

      //2.2 判断配菜所属订单是否已处于"配菜中"订单队列，如果不属于则判断"配菜中"订单队列是否已满不许插队、或者未满则插队到"配菜中"订单队列尾部
      List<Long> preparingOrderQueue = parameterService.getListByCode("preparingOrderQueue", Long.class);

      if (!preparingOrderQueue.contains(kcKscToJump.getBsId())) {
        if (preparingOrderQueue.size() >= kitchenQueueUtil.getMaxPreparingSize() + kitchenQueueUtil.getMaxJumpOrderCount()) {
          throw new ApiRRException(HttpStatus.BAD_REQUEST, "订单排队已满，不能再插队！");
        }
      }

      //3. 插入该菜品，并将厨房制作流水合并到配菜窗口列表
      kitchenQueueUtil.addToJumpItems(kcKscId);
      int windowSize = kitchenQueueUtil.getPrepareWindowSizes().get(windowIndex) +
          kitchenQueueUtil.getMaxJumpItemCount();
      int mergeCount = kitchenQueueUtil.getPrepareMergeCounts().get(windowIndex);
      kitchenQueueUtil.mergeKcKscItemToWindowList(windowList, kcKscToJump, windowSize, mergeCount);

      //4. 保存插队结果
      parameterService.saveByCode(windowName,  JSONUtil.toJSONString(windowList));

      //如果菜品已经在等待队列，则从等待队列删除
      List<Long> waitingIds = parameterService.getListByCode("itemWaitingList", Long.class);
      if (waitingIds.contains(kcKscId)) {
        waitingIds.remove(kcKscId);
        parameterService.saveByCode("itemWaitingList",  JSONUtil.toJSONString(waitingIds));
      }
    }
    //5. 消息通知配菜、传菜界面刷新
    messagingTemplate.convertAndSend("/prepare_refresh", "");
    messagingTemplate.convertAndSend("/passon_refresh", "");
  }

  @Override
  public List<ProductVo> queryPassed(@NotNull Long shopId, String keyword) {
    List<ProductVo> result = kscService.queryCancelableItems(shopId, CommonConstants.KitchenFlg.PRODUCED, null);
    for (int i = result.size() - 1; i >= 0; i--) {
      ProductVo productVo = result.get(i);
      if (!StringUtils.isBlank(keyword) &&
          !productVo.getItemName().contains(keyword) &&
          !productVo.getTableName().contains(keyword)) {
        result.remove(i);
      }
    }
    return result;

  }

  @Override
  public List<ProductVo> queryKscHistory(Long shopId, Boolean needException) {
    LocalDateTime now = LocalDateTime.now();
    QueryWrapper<TbKcKscTime> wrapper = new QueryWrapper<TbKcKscTime>();
    wrapper.between("call_up_time", now.with(LocalTime.MIN), now.with(LocalTime.MAX));
    wrapper.gt("order_call_time", 1020);
    wrapper.apply("EXTRACT(epoch from (now()-call_up_time)) < 12*3600");
    wrapper.orderBy(true, true, "call_up_time");
    List<TbKcKscTime> tbKcKscTimeList = tbKcKscTimeService.list(wrapper);
    List<Long> kcKscIds = new ArrayList<>();
    Map<Long, TbKcKscTime> tbKcKscTimeHashMap = new HashMap<>();
    for (TbKcKscTime tbKcKscTime : tbKcKscTimeList) {
      kcKscIds.add(tbKcKscTime.getId());
      tbKcKscTimeHashMap.put(tbKcKscTime.getId(), tbKcKscTime);
    }
    // 2. 表kc_ksc 数据获取加工时间
    QueryWrapper<KcKsc> kcKscQueryWrapper = new QueryWrapper<>();
    kcKscQueryWrapper.in("id",kcKscIds);
    kcKscQueryWrapper.orderBy(true,true,"call_up_time");
    //只查询8小时内的记录
    List<KcKsc> kcKscList = kcKscMapper.selectList(kcKscQueryWrapper);
    List<ProductVo> result = new ArrayList<>();

    QueryWrapper<ArchItem> archItemQueryWrapper = new QueryWrapper<ArchItem>();
    List<ArchItem> archItemList = archItemMapper.selectList(archItemQueryWrapper);
    Map<Long,ArchItem> archItemMap =archItemList.stream().collect(Collectors.toMap(ArchItem::getId, Function.identity()));

    QueryWrapper<ArchPoint> archPointQueryWrapper = new QueryWrapper<ArchPoint>();
    List<ArchPoint> archPointList = archPointMapper.selectList(archPointQueryWrapper);
    Map<Long,ArchPoint> archPointMap =archPointList.stream().collect(Collectors.toMap(ArchPoint::getId, Function.identity()));
    for (KcKsc kcKsc : kcKscList) {
      ProductVo product = new ProductVo();
      TbKcKscTime tbKcKscTime = tbKcKscTimeHashMap.get(kcKsc.getId());
      if (tbKcKscTime == null) {
        continue;
      }
      List<Long> kscIds = new ArrayList<>();
      kscIds.add(kcKsc.getId());
      product.setKscIds(kscIds);
      product.setItemId(kcKsc.getItemId().toString());
      product.setStatus(kcKsc.getKitchenFlg());
      product.setBsId(kcKsc.getBsId());
      BizBs bizBs = bizBsMapper.selectById(kcKsc.getBsId());
      if (bizBs == null) continue;

      ArchPoint archPoint = archPointMap.get(bizBs.getPointId());
      if (archPoint != null){
        product.setTableNo(archPoint.getCode());
        product.setTableName(archPoint.getName());
      }
      ArchItem archItem = archItemMap.get(kcKsc.getItemId());
      if (archItem == null) {
        product.setItemName(kcKsc.getTempItemName());
        product.setItemId(kcKsc.getItemId() + kcKsc.getTempItemName());
      } else {
        product.setItemId(archItem.getId().toString());
        product.setItemName(archItem.getName());
      }
      product.setOrigQty(kcKsc.getOrigQty());
      product.setLastQty(kcKsc.getLastQty());
      product.setMethod(CommonUtil.methodText2List(kcKsc.getMethodText()));
      product.setOrderTime(kcKsc.getCreateTime());
      product.setOrderCallTime(tbKcKscTime.getOrderCallTime());

      product.setCallUpTime(kcKsc.getCallUpTime());
      product.setCookFinishTime(kcKsc.getCookFinishTime());
      product.setServeFinishTime(kcKsc.getServeFinishTime());
      result.add(product);
    }
    return result;
  }

}
